package server;

import java.net.InetSocketAddress;
import java.security.PrivateKey;
import java.security.PublicKey;

import exceptions.InvalidPacketContentException;

import packets.Packet;
import packets.client.Login;
import packets.client.PublicKeyPair;
import packets.client.Registration;
import packets.server.Certificate;
import packets.server.OkPacket;
import packets.server.UserDetails;
import packets.server.UserList;


/**
 * Protocol for the server , receives a Packet , and returns a Packet as a reply
 */
public class CAProtocol {
	private InetSocketAddress _ipaddr;
	private SharedStuff _stuff;
	private PrivateKey _serverSigningKey;

	/**
	 * Creates an instance of {@link CAProtocol}
	 * @param inetSocketAddress the client's IP address and port
	 * @param stuff the server's shared data
	 * @param serverSigningKey key to sign certificates
	 */
	public CAProtocol(InetSocketAddress inetSocketAddress , SharedStuff stuff , PrivateKey serverSigningKey){
		_ipaddr = inetSocketAddress;
		_stuff = stuff;
		_serverSigningKey = serverSigningKey;
	}

	/**
	 * receives a Packet , and returns a Packet as a reply according to the protocol
	 * LOGIN      -> CERTIFICATE or ERROR
	 * DELETE     -> OK or ERROR
	 * LOGOUT     -> OK or ERROR
	 * ALLUSERS   -> USERLIST
	 * WHOSONLINE -> USERLIST
	 * DETAILS    -> USERDETAILS or ERROR
	 * CHAT		  -> CERTIFICATE
	 * @param message the message the server needs to reply to
	 * @return the server's reply according to the protocol
	 */
	public Packet handlePacketMessage(Packet message){
		try{
			switch (message.getType()){
			case Packet.LOGIN:					//login message
				return login(message.getLoginContent());
			case Packet.DELETE:					//delete message
				return delete();
			case Packet.LOGOUT:					//logout message
				return logout();
			case Packet.ALLUSERS:				//allusers message
				return allUsers();
			case Packet.WHOSONLINE:				//whosonline message
				return whosonline();
			case Packet.DETAILS:				//details request
				return details(message.getIdContent());
			case Packet.CHAT:					//chat request
				return chat(message.getIdContent());
			case Packet.VERIFY:					//chat request
				return verify(message.getKeyContent());
			default: throw new InvalidPacketContentException("Unknown content - " + message.getType());
			}
		}catch(InvalidPacketContentException e){
			System.out.println("this cant happen!!!");
			return null;
		}
	}

	/**
	 * returns a reply to a verify request (checks if a key is not in the CRL)
	 * @param keyContent the key to verify
	 * @return true if the key is not in the CRL
	 */
	private Packet verify(PublicKey keyContent) {
		if (_stuff.isLegalKey(keyContent)){
			return new OkPacket();
		}
		else return new Packet("keyincrl");
	}

	/**
	 * receives a registration request and returns the appropriate reply (either a certificate or an error message) , packet in a Packet
	 * @param clientId client ID for registration
	 * @param regData registration data , containing information about the client and his password
	 * @param publicKeyPair client's public keys
	 * @param certificate client's certificate which was produced by the server
	 * @return the reply for the registration request
	 */
	public Packet register(int clientId, Registration regData , PublicKeyPair publicKeyPair, Certificate certificate){
		if (_stuff.registerUser(clientId, regData.getName(), regData.getPassword() , regData. getBday() ,regData.getCountry() ,regData.getCity(),regData.getPicture(), publicKeyPair, certificate)){
			return _stuff.loginUser(clientId, regData.getPassword(), _ipaddr, _serverSigningKey);
		}else {
			return new Packet("useralreadyexists");
		}
	}

	/**
	 * handles login request
	 * @param loginData login request details
	 * @return the answer for the request
	 */
	private Packet login(Login loginData){
		return _stuff.loginUser(loginData.getId(), loginData.getPassword() , _ipaddr , _serverSigningKey);
	}


	/**
	 * handles delete request
	 * @return the answer for the request
	 */
	private Packet delete(){
		if (_stuff.delete(_ipaddr)){
			return new OkPacket();
		}else{
			return new Packet("badrequest");
		}
	}

	/**
	 * handles logout request
	 * @return the answer for the request
	 */
	private Packet logout(){
		if (_stuff.logoutUser(_ipaddr)){
			return new OkPacket();
		}else{
			return new Packet("invalidaction");
		}
	}

	/**
	 * handles alluser request - request for a list of the existing users
	 * @return the answer for the request - a list of users
	 */
	private Packet allUsers(){
		UserList allUsers = _stuff.getAllUsers();
		return new Packet(allUsers);
	}

	/**
	 * handles whosonline request - request for a list of the online users
	 * @return the answer for the request - a list of the online users
	 */
	private Packet whosonline(){
		UserList loggedIn = _stuff.getOnline();
		return new Packet(loggedIn);
	}

	/**
	 * handles details request - returns the requested user details
	 * @param messages the command to fetch the user details , containing the request parameters
	 * @return the response to the request - a Packet with the {@link UserDetails} or an error message
	 */
	private Packet details(int id){
		User user = _stuff.getDetails(id);
		if (user!=null){
			return new Packet(new UserDetails(id, user));
		}else{
			return new Packet("illegaluser");
		}
	}

	/**
	 * handles a chat request
	 * @param messages the chat request details (containing the ID of the requested client)
	 * @return a {@link Certificate} {@link Packet}, if the client exists and logged in, or an error message if there is an error
	 */
	private Packet chat(int id) {
		Certificate c = _stuff.getCertificate(id);
		if (c!=null){
			return new Packet(c);
		} else{
			return new Packet("userloggedoff");
		}
	}

}
